#include "gtlGame.h"
#include "gtlResource.h"
#include <ctime>

namespace GTL
{
	namespace Engine
	{
		BackendInterface* Backend = 0;

		Game::Game() : mCurrentMode(0)
		{
		}

		Game::~Game()
		{
			
		}

		void Game::UnloadCurrentMode()
		{
			if (mCurrentMode)
			{
				mCurrentMode->Unload();
				mCurrentMode->UnloadMode();

				GTL::AutoMembers::Producible::Destroy(mCurrentMode);
			}
		}

		int Game::Run()
		{
			RegisterService(&mPreUpdateService);
			RegisterService(&mLoaderService);
			RegisterService(&mNetworkService);
			RegisterService(&mUpdateService);
			RegisterService(&mPostUpdateService);
			RegisterService(&mDrawService);
			RegisterService(&mPostDrawService);
			RegisterService(&mPostAllService);
			Backend->RegisterServices();
			for (service_set::iterator i = mServices.begin(); i!=mServices.end(); ++i)
			{
				Service* service = *i;
				service->Init(this);
			}

			int retValue = StartGame();
			if (retValue) return retValue;

			double lastClock = Backend->GetTimeSec();
			bool run = true;
			while (run)
			{
				double t = Backend->GetTimeSec();
				double dt = t-lastClock;
				lastClock = t;

				Backend->LogTrace("Starting service iteration...");
				for (service_set::iterator i = mServices.begin(); i!=mServices.end(); ++i)
				{
					Service* service = *i;
					Backend->LogTrace(service->GetName());
					retValue = service->Do((float)dt);
					if (retValue) run = false;
				}
				Backend->LogTrace("Done.");
			}

			if (mCurrentMode)
				mCurrentMode->Stop();
			UnloadCurrentMode();

			QuitGame();

			for (service_set::iterator i = mServices.begin(); i!=mServices.end(); ++i)
			{
				Service* service = *i;
				service->Shutdown();
			}
			Backend->Destroy();
			return retValue;
		}

		void Game::RegisterService( Service* service )
		{
			mServices.insert(service);
		}

		void Game::DoLoading()
		{
			if (mNext.NextMode != 0)
			{
				if (mCurrentMode) 
					mCurrentMode->Stop();

				if (mNext.NextMode != mCurrentModeID)
				{
					UnloadCurrentMode();
					mCurrentMode = CreateMode(mNext.NextMode, mNext.Arg1, mNext.Arg2);

					if (mCurrentMode)
					{
						mCurrentModeID = mNext.NextMode;
						mNext.Reset();
						mCurrentMode->SetGame(this);
						mCurrentMode->LoadMode();
						mCurrentMode->Load();
					} else
					{
						Backend->LogError("Got request for mode %i which the game cannot create for some reason (args: %i, %i)", mNext.NextMode, mNext.Arg1, mNext.Arg2);
						mNext.Reset();
					}
				}

				mCurrentMode->Start();
			}
		}

		void Game::SetNext( int id, int arg1 /*= 0*/, int arg2 /*= 0*/ )
		{
			mNext.NextMode = id;
			mNext.Arg1 = arg1;
			mNext.Arg2 = arg2;
		}

		Service* Game::GetService( const char* name )
		{
			for (service_set::iterator i = mServices.begin(); i!=mServices.end(); ++i)
			{
				Service* service = *i;
				if (!strcmp(service->GetName(), name)) return service;
			}
			return 0;
		}

		///************************************************************************/
		///* Mode                                                                 */
		///************************************************************************/

		int Mode::UnloadMode()
		{
			return 0;
		}

		int Mode::LoadMode()
		{
			MainGUI.SetEventBuffer(&Events); 
			return 0;
		}

		int Mode::OnCreate()
		{
			for (GTL::AutoMembers::AutoMember* i=mMembers; i; i = i->Next)
			{
				if (i->GetBaseType() == 'reso')
				{
					Resource* r = (Resource*)i;
					r->OnLoad();
				}
			}
			return 0;
		}

		int Mode::OnDestroy()
		{
			for (GTL::AutoMembers::AutoMember* i=mMembers; i; i = i->Next)
			{
				if (i->GetBaseType() == 'reso')
				{
					Resource* r = (Resource*)i;
					r->Unload();
				}
			}
			return 0;
		}

		///************************************************************************/
		///* Services                                                             */
		///************************************************************************/

		int Game::PreAllService::Do( float)
		{
			return 0;
		}

		int Game::LoaderService::Do( float )
		{
			mGame->DoLoading();
			return 0;
		}

		int Game::PreUpdateService::Do( float dt )
		{
			if (mGame->mCurrentMode)
			{
				Mode* mode = mGame->mCurrentMode;

				/// GUI Input & Update
				GUI::Control& maingui = mode->MainGUI;
				maingui.MouseMove(Backend->MouseState[0].X, Backend->MouseState[0].Y);

				if (Backend->MouseState[0].Buttons&1 && !(Backend->MouseState[1].Buttons&1))
					maingui.MouseDown(Backend->MouseState[0].X, Backend->MouseState[0].Y);
				else if (!(Backend->MouseState[0].Buttons&1) && (Backend->MouseState[1].Buttons&1))
					maingui.MouseUp(Backend->MouseState[0].X, Backend->MouseState[0].Y);

				for (int i=0; i<256; i++)
				{
					if (Backend->KeyboardState[0].Keys[i] && !Backend->KeyboardState[1].Keys[i])
						maingui.KeyDown(i);
					if (Backend->KeyboardState[1].Keys[i] && !Backend->KeyboardState[0].Keys[i])
						maingui.KeyUp(i);
				}

				for (size_t i=0; i<mode->Events.Count; i++)
				{
					GameEvent& event = mode->Events.Events[i];
					if (event.EventID == Core::Event::CharEntered)
					{
						maingui.CharEntered(event.AsChar.Key, event.AsChar.Char);
					}
				}

				maingui.Update(dt);

				/// Events

				while (mGame->mCurrentMode->Events.Count)
				{
					GameEvent event = mGame->mCurrentMode->Events.PopEvent();
					if (mGame->mCurrentMode->Event(event) != 0)
					{
						mGame->Event(event);
					}
				}
			}
			return 0;
		}

		int Game::UpdateService::Do( float dt )
		{
			if(mGame->mCurrentMode)
				return mGame->mCurrentMode->Update(dt);
			return 0;
		}

		int Game::PostUpdateService::Do( float )
		{
			return 0;
		}

		int Game::DrawService::Do( float dt )
		{
			int result = 0;
			if (mGame->mCurrentMode)
			{
				GTL::Engine::GUI::IM::Start();
				result = mGame->mCurrentMode->Draw(dt);
				GTL::Engine::GUI::IM::End();
			}
			return result;
		}

		int Game::PostDrawService::Do( float )
		{
			if (mGame->mCurrentMode)
			{
				mGame->mCurrentMode->MainGUI.Draw();
			}
			Backend->UnuseShader();
			return 0;
		}

		int Game::PostAllService::Do( float )
		{
			if (mGame->mCurrentMode)
			{
				if (Logging::CanLog(Logging::Warning) && mGame->mCurrentMode->Events.Count != 0)
					Backend->LogWarning("Events left at end of game loop.");

				mGame->mCurrentMode->Events.ClearEvents();
			}
			return 0;
		}
	}
}